package spring.cloud.tasks.common.curator;

import com.google.common.base.Strings;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.ACLProvider;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.curator.utils.CloseableUtils;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.data.ACL;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * <pre>
 *     ZK生成环境进行安全认证流程如下:
 *     setAcl /path digest:<用户名>:<用户名:密码的密文>:crwda
 *     setAcl / digest:cxl:d3fQ69gO+3LN757s9rgebiynbQY=:crwda
 *     addauth digest cxl:cxl  登录方式(命令行或者CuratorFramework)
 * </pre>
 */
@Slf4j
public class CuratorFactory {
    /**
     * 创建digest的方法,想要生成digest可以复制该方法进行生成。
     */
    public static void main(String[] args) throws NoSuchAlgorithmException {
        String usernamePasswod = "cxl:cxl";//这里是cxl:cxl<用户名:密码>的密文:d3fQ69gO+3LN757s9rgebiynbQY=
        byte[] digest = MessageDigest.getInstance("SHA1").digest(usernamePasswod.getBytes());
        String encodeString = Base64.getEncoder().encodeToString(digest);
        System.out.println(encodeString);
    }
    //

    private static final ConcurrentHashMap<String, CuratorFramework> namespaceToCuratorFrameworkrMap = new ConcurrentHashMap<>();


    /**
     * @param curatorInfo
     * @param shortTime   是否设置短时间配置
     * @return
     * @throws Exception
     */
    public static Curator getCurator(@NonNull CuratorInfo curatorInfo, boolean shortTime) throws RuntimeException {
        String addressList = curatorInfo.getAddressList();
        String digest = curatorInfo.getDigest();
        synchronized (curatorInfo.getNamespaceKey().intern()) {
            CuratorFramework curatorFramework = namespaceToCuratorFrameworkrMap.get(curatorInfo.getNamespaceKey());
            if (curatorFramework != null) {
                if (curatorFramework != null && curatorFramework.getZookeeperClient().isConnected()) {
                    Curator curator = new CuratorImpl(curatorInfo, curatorFramework);
                    return curator;
                } else {
                    namespaceToCuratorFrameworkrMap.remove(curatorInfo.getNamespaceKey());
                    curatorFramework.close();
                }
            }
            curatorFramework = CuratorFactory.connect(addressList, digest, curatorInfo.getNamespaceKey(), shortTime);
            if (curatorFramework != null && curatorFramework.getZookeeperClient().isConnected()) {
                namespaceToCuratorFrameworkrMap.put(curatorInfo.getNamespaceKey(), curatorFramework);
                Curator curator = new CuratorImpl(curatorInfo, curatorFramework);
                return curator;
            }
            return null;
        }
    }

    /**
     * @param connectString 127.0.0.1:2181
     * @param digest        cxl:cxl
     */
    public static final boolean shortTime_true = true;
    public static final boolean shortTime_false = false;

    public static CuratorFramework connect(final String connectString, final String digest, final String nameSpace, boolean shortTime) {
        CuratorInfo.CuratorSetting curatorSetting = null;
        if (shortTime) {
            curatorSetting = new CuratorInfo.ShortTimeCuratorSetting();
        } else {
            curatorSetting = new CuratorInfo.LongTimeCuratorSetting();
        }
        CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder()//
                .connectString(connectString)//
                .connectionTimeoutMs(curatorSetting.connectionTimeoutSeconds * CuratorInfo.CuratorSetting.MILLISECONDS)//
                .sessionTimeoutMs(curatorSetting.sessionTimeoutSeconds * CuratorInfo.CuratorSetting.MILLISECONDS)//
                .retryPolicy(new ExponentialBackoffRetry(
                        curatorSetting.baseSleepTimeSeconds * CuratorInfo.CuratorSetting.MILLISECONDS,//
                        curatorSetting.maxRetries, //
                        curatorSetting.maxSleepSeconds * CuratorInfo.CuratorSetting.MILLISECONDS));//
        if (nameSpace != null) {
            builder.namespace(nameSpace);
        }
        if (!Strings.isNullOrEmpty(digest)) {
            builder.authorization("digest", digest.getBytes(StandardCharsets.UTF_8)).aclProvider(new ACLProvider() {
                @Override
                public List<ACL> getDefaultAcl() {
                    return ZooDefs.Ids.CREATOR_ALL_ACL;
                }

                @Override
                public List<ACL> getAclForPath(final String path) {
                    return ZooDefs.Ids.CREATOR_ALL_ACL;
                }
            });
        }
        CuratorFramework curatorFramework = builder.build();
        curatorFramework.start();
        boolean established = false;
        try {
            established = curatorFramework.blockUntilConnected(curatorSetting.maxWaitTimeSeconds, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            log.warn(e.getMessage());//一般不会不会出现这个异常
        }
        if (!established) {
            CloseableUtils.closeQuietly(curatorFramework);
            return null;
        }
        //为了避免连接成功但是没有认证的情况出现，故需要在连接成功后调用API进行测试 如果不报错那代表认证成功。
        if (Strings.isNullOrEmpty(nameSpace)) {
            try {
                String test = "/test";
                if (curatorFramework.checkExists().forPath(test) == null) {
                    curatorFramework.create().forPath(test);
                }
                curatorFramework.create().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(test);
                curatorFramework.delete().forPath(test);
            } catch (Exception e) {
                log.error(e.getMessage());
                established = false;
            }
        }
        if (!established) {
            CloseableUtils.closeQuietly(curatorFramework);
            return null;
        } else {
            return curatorFramework;
        }
    }
}

